package com.xl.basic.aop.aspect;


import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xl.basic.annotation.LogIgnored;
import com.xl.basic.entity.Result;
import com.xl.basic.entity.ResultEnums;
import com.xl.basic.entity.UserInfo;
import com.xl.basic.utils.JacksonUtils;
import com.xl.basic.utils.LoginUtils;
import com.xl.basic.utils.ServletUtils;
import com.xl.dao.mapper.XlVisitLogMapper;
import com.xl.dao.domain.XlVisitLog;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.concurrent.ExecutorService;

/**
 * 日志切面
 * 记录进入controller执行的日志
 *
 * @author 钟勇
 * Create in 2023/8/18 17:52
 */
@Component
@Aspect
@Order(0)
@RequiredArgsConstructor
public class LogAspect {

    private final XlVisitLogMapper logMapper;
    private final ExecutorService executorService;

    @Around("execution(* com.xl..*.controller..*.*(..))")
    public Object doAround(ProceedingJoinPoint point) throws Throwable {
        LocalDateTime beginTime = LocalDateTime.now();
        String ip = ServletUtils.getClientIP();

        Object res;
        Throwable throwable = null;
        try {
            res = point.proceed();
        } catch (Throwable e) {
            res = ResultEnums.Exception.getResult(e.getMessage());
            throwable = e;
        }

        LocalDateTime endTime = LocalDateTime.now();

        Result<UserInfo> userInfoRes = LoginUtils.getUserInfo();
        UserInfo userInfo = userInfoRes.getResult();

        //在线程池中执行写日志操作
        Object finalRes = res;
        executorService.execute(() -> {
            Signature signature = point.getSignature();
            ObjectMapper objectMapper = JacksonUtils.objectMapper();


            Class<?> declaring = signature.getDeclaringType();
            String controllerDescription = null;
            if (declaring != null) {
                //忽略记录日志
                if (declaring.getAnnotation(LogIgnored.class) != null) {
                    return;
                }

                Tag tag = declaring.getAnnotation(Tag.class);
                if (tag != null) {
                    controllerDescription = tag.name();
                }
            }

            Method method = ((MethodSignature) signature).getMethod();
            String actionDescription = null;
            if (method != null) {
                //忽略记录日志
                if (method.getAnnotation(LogIgnored.class) != null) {
                    return;
                }
                Operation operation = method.getAnnotation(Operation.class);
                if (operation != null) {
                    actionDescription = operation.summary();
                }
            }

            //获取当前请求的所有参数
            String parameter = null;
            Object[] args = point.getArgs();
            if (args != null && args.length > 0) {
                try {
                    parameter = objectMapper.writeValueAsString(args);
                } catch (JsonProcessingException ignored) {
                }
            }

            //获取当前请求的结果
            String resJson = null;
            Result<Object> basicRes = null;
            if (finalRes != null) {
                try {
                    resJson = objectMapper.writeValueAsString(finalRes);
                    basicRes = objectMapper.readValue(resJson, new TypeReference<>() {
                    });

                } catch (JsonProcessingException ignored) {
                }
            }

            XlVisitLog log = new XlVisitLog()
                    .setControllerName(signature.getDeclaringTypeName())
                    .setControllerDescription(controllerDescription)
                    .setActionName(signature.getName())
                    .setActionDescription(actionDescription)
                    .setBeginTime(beginTime)
                    .setEndTime(endTime)
                    .setParameter(parameter)
                    .setResponseCode(basicRes != null ? basicRes.getCode() : null)
                    .setResponseMessage(basicRes != null ? basicRes.getMessage() : null)
                    .setResponse(resJson)
                    .setUserId(userInfo != null ? userInfo.getUserId() : null)
                    .setUserName(userInfo != null ? userInfo.getUserName() : null)
                    .setClientIp(ip);

            try{
                logMapper.insert(log);
            }catch (Exception ignored){

            }
        });


        if (throwable != null) {
            throw throwable;
        }

        return res;
    }
}
